package com.jfs.security.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jfs.constant.SecurityConstant;
import com.jfs.entity.Role;
import com.jfs.entity.User;
import com.jfs.exception.UserNotExistException;
import com.jfs.mapper.UserMapper;
import com.jfs.result.Result;
import com.jfs.result.ResultCode;
import com.jfs.security.authentication.GrantedAuthorityImpl;
import com.jfs.security.authentication.JwtUser;
import com.jfs.security.authentication.SessionManager;
import com.jfs.security.dto.DropUserDto;
import com.jfs.security.dto.UserDto;
import com.jfs.security.service.RoleService;
import com.jfs.security.service.UserService;
import com.jfs.security.vo.*;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * @Description 用户操作数据库实现
 * @E-mail cwm3412638@163.com
 * @Author cwm
 * @Date 2022/8/5
 */
@Service
@Slf4j
@AllArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    private final RoleService roleService;
    private final PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();

    /**
     * 根据用户名查询用户
     *
     * @param username
     * @return
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, BadCredentialsException {
        if (StrUtil.isEmpty(username)) {
            throw new UsernameNotFoundException("用户名不能为空");
        }
        User user = this.baseMapper.selectOne(Wrappers.<User>lambdaQuery().eq(User::getUsername, username));
        if (Objects.isNull(user)) {
            throw new UserNotExistException(ResultCode.USER_NOT_EXIST.getMessage(), ResultCode.USER_NOT_EXIST.getCode());
        } else {
            if (!user.getEnable()) {
                throw new LockedException(ResultCode.USER_ACCOUNT_DISABLED.getMessage());
            } else {
                Role role = roleService.getById(user.getRoleId());
                List<GrantedAuthorityImpl> authorities = new ArrayList<>();
                GrantedAuthorityImpl grantedAuthority = null;
                if (Objects.nonNull(role)) {
                    grantedAuthority = new GrantedAuthorityImpl(role.getRoleKey(), role.getAuthority());
                }
                authorities.add(grantedAuthority);
                return new JwtUser(user.getId(), user.getRoleId(), user.getPhone(), user.getNickName(), role, user.getUsername(), user.getPassword(), user.getEnable(), authorities);
            }
        }
    }

    /**
     * 分页条件查询用户信息列表
     *
     * @param userListVo
     * @return
     */
    @Override
    public IPage<UserDto> list(UserListVo userListVo) {
        Page<User> page = this.page(new Page<User>(userListVo.getPageNum(), userListVo.getPageSize()),
                Wrappers.<User>lambdaQuery()
                        //根据状态查询
                        .eq(Objects.nonNull(userListVo.getEnable()), User::getEnable, userListVo.getEnable())
                        //根据关键字搜索 用户名和用户昵称
                        .and(StrUtil.isNotEmpty(userListVo.getSearchKey()), wrap ->
                                wrap.like(User::getNickName, userListVo.getSearchKey())
                                        .or()
                                        .like(User::getUsername, userListVo.getSearchKey()))
                        .orderByDesc(User::getCreateTime));
        List<User> records = page.getRecords();
        List<UserDto> dtoList = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(records)) {
            for (User user : records) {
                UserDto userDto = new UserDto();
                BeanUtils.copyProperties(user, userDto);
                Role role = this.roleService.getById(user.getRoleId());
                userDto.setRole(role);
                dtoList.add(userDto);
            }
        }
        Page<UserDto> userDtoPage = new Page<>();
        BeanUtils.copyProperties(page, userDtoPage);
        userDtoPage.setRecords(dtoList);
        return userDtoPage;
    }

    /**
     * 添加用户
     *
     * @param addUserVo
     */
    @Override
    @Transactional
    public Result<Object> save(AddUserVo addUserVo) {
        //判断添加是否是超级管理员角色用户
        Role role = this.roleService.getById(addUserVo.getRoleId());
        if (SecurityConstant.SUPER_ADMIN.equals(role.getRoleKey())) {
            return Result.failed("超级用户已存在,不能进行添加");
        } else {
            //判断用户名是否唯一
            User onlyUser = this.getOne(Wrappers.<User>lambdaQuery().eq(User::getUsername, addUserVo.getUsername()));
            if (Objects.nonNull(onlyUser)) {
                return Result.failed("用户名已存在");
            }
            User user = new User();
            BeanUtils.copyProperties(addUserVo, user);
            //密码加密
            addUserVo.setPassword(passwordEncoder.encode(addUserVo.getPassword()));
            boolean save = this.save(user);
            if (save) {
                return Result.success("添加用户成功", null);
            }
            return Result.failed("添加用户失败");
        }
    }

    /**
     * 修改用户
     *
     * @param updateUserVo
     */
    @Override
    @Transactional
    public Result<Object> update(UpdateUserVo updateUserVo) {
        User user = new User();
        BeanUtils.copyProperties(updateUserVo, user);
        boolean b = this.updateById(user);
        if (b) {
            return Result.success("修改用户信息成功", null);
        }
        return Result.failed("修改用户信息失败");

    }

    /**
     * 禁用用户
     *
     * @param disableUserVo
     */
    @Override
    @Transactional
    public Result<Object> disable(DisableUserVo disableUserVo) {
        //判断禁用用户是否属于超级管理员
        User user = this.getById(disableUserVo.getUserId());
        Role role = this.roleService.getById(user.getRoleId());
        if (SecurityConstant.SUPER_ADMIN.equals(role.getRoleKey())) {
            return Result.failed("超级管理员不能被禁用");
        }
        boolean update = this.update(Wrappers.<User>lambdaUpdate()
                .set(User::getEnable, Boolean.FALSE)
                .eq(User::getId, disableUserVo.getUserId())
        );
        if (update) {
            return Result.success("禁用用户成功", null);
        }
        return Result.failed("禁用用户失败");
    }

    /**
     * 删除用户
     *
     * @param disableUserVo
     */
    @Override
    @Transactional
    public Result<Object> delete(DisableUserVo disableUserVo) {
        //判断删除用户是否属于超级管理员
        User user = this.getById(disableUserVo.getUserId());
        Role role = this.roleService.getById(user.getRoleId());
        if (SecurityConstant.SUPER_ADMIN.equals(role.getRoleKey())) {
            return Result.failed("超级管理员不能被删除");
        }
        boolean b = this.removeById(disableUserVo.getUserId());
        if (b) {
            return Result.success("删除用户成功", null);
        }
        return Result.failed("删除用户失败");
    }

    /**
     * 修改密码
     *
     * @param loginUserEntity
     * @param updateUserPasswordVo
     * @return
     */
    @Override
    @Transactional
    public Result<Object> updatePassword(LoginUserEntity loginUserEntity, UpdateUserPasswordVo updateUserPasswordVo) {
        if (!updateUserPasswordVo.getNewPassword().equals(updateUserPasswordVo.getAgainNewPassword())) {
            return Result.failed("输入的两次密码不一致");
        }
        User user = this.getById(loginUserEntity.getId());
        if (!passwordEncoder.matches(updateUserPasswordVo.getOldPassword(), user.getPassword())) {
            return Result.failed("原密码不正确,修改失败");
        }
        boolean update = this.update(Wrappers.<User>lambdaUpdate()
                .set(User::getPassword, passwordEncoder.encode(updateUserPasswordVo.getNewPassword()))
                .eq(User::getId, user.getId())
        );
        if (update) {
            //清除当前用户会话信息
            SessionManager.cleanSession(loginUserEntity.getUsername());
            return Result.success("修改密码成功", null);
        }
        return Result.failed("修改密码失败");
    }

    @Override
    public Result<Object> logOut(LoginUserEntity loginUserEntity) {
        SessionManager.cleanSession(loginUserEntity.getUsername());
        return Result.success("退出成功");
    }

    @Override
    public Result<Object> info(LoginUserEntity loginUserEntity) {
        return Result.success(loginUserEntity);
    }

    @Override
    @Transactional
    public Result<Object> enable(DisableUserVo disableUserVo) {
        User user = this.getById(disableUserVo.getUserId());
        boolean update = this.update(Wrappers.<User>lambdaUpdate()
                .set(User::getEnable, Boolean.TRUE)
                .eq(User::getId, user.getId()));
        if (update) {
            return Result.success("启用用户成功", null);
        }
        return Result.failed("启用用户失败");
    }

    /**
     * 获取用户下拉列表
     * @return
     */
    @Override
    public List<DropUserDto> drop() {
        List<DropUserDto> dropUserDtos = new ArrayList<>();
        List<User> userList = this.list(Wrappers.<User>lambdaQuery()
                .select(User::getId, User::getUsername)
                .eq(User::getEnable, Boolean.TRUE)
        );
        if (CollectionUtil.isNotEmpty(userList)) {
            for (User user : userList
            ) {
                DropUserDto dropUserDto = new DropUserDto();
                BeanUtils.copyProperties(user, dropUserDto);
                dropUserDtos.add(dropUserDto);
            }
        }
        return dropUserDtos;
    }
}
